package common.unit;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.map.SerializationConfig;
import org.glassfish.jersey.client.proxy.WebResourceFactory;
import common.logging.Item;

public class ClientSession
{

    private MultivaluedHashMap<String, Object> headers;
    private ArrayList<Cookie> cookies;
    private WebTarget target;
    private final Map<Class<?>, Object> handlers = new HashMap<Class<?>, Object>();
    private final ObjectMapper mapper = new ObjectMapper();
    private final ArrayList<Item> logResponseItems = new ArrayList<Item>();
    private boolean printDebugRealTime;
    private int lastResponseCode;
    ObjectWriter ow;

    public ClientSession(String server) throws Exception
    {
        String isDebug = System.getProperty("printDebug");
        printDebugRealTime = Boolean.parseBoolean(isDebug);

        mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false).configure(
            SerializationConfig.Feature.INDENT_OUTPUT, true);
        ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        ClientBuilder builder = ClientBuilder.newBuilder();
        builder.register(new ManipulateMessageBodyReader(logResponseItems));
        builder.register(new ClientResponseFilter()
        {
            public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
                            throws IOException
            {
                // A bit of hack for logging purposes. If the response has a payload, we will submit
                // the log entity it in the messagebodyreader.
                // else we submit the log entity here.
                // BUG: Sometimes responseContext.hasEntity() returns true even though there is no
                // payload. The messageBodyReader doesn't get it.
                // This is why we have added response_to_uri, so that the output is easily
                // understandable.
                if (!responseContext.hasEntity())
                {
                    logResponseItems.add(new Item("response_to_uri", requestContext.getUri()));
                    logResponseItems.add(new Item("statusCode", responseContext.getStatus()));

                    Item[] array = new Item[logResponseItems.size()];
                    array = logResponseItems.toArray(array);
                    logResponseItems.clear();
                }
                else
                {
                    logResponseItems.add(new Item("response_to_uri", requestContext.getUri()));
                    logResponseItems.add(new Item("statusCode", responseContext.getStatus()));
                }
                lastResponseCode = responseContext.getStatus();
            }
        });

        builder.register(new ClientRequestFilter()
        {
            public void filter(ClientRequestContext requestContext) throws IOException
            {
                final ArrayList<Item> logRequestItems = new ArrayList<Item>();
                logRequestItems.add(new Item("request_uri", requestContext.getUri()));
                logRequestItems.add(new Item("method", requestContext.getMethod()));
                if (requestContext.getHeaders().size() > 0)
                {
                    for (Entry<String, List<Object>> e : requestContext.getHeaders().entrySet())
                    {
                        for (Object v : e.getValue())
                        {
                            logRequestItems.add(new Item(e.getKey(), v.toString()));
                        }
                    }
                    if (requestContext.hasEntity())
                    {
                        String json = ow.writeValueAsString(requestContext.getEntity());
                        logRequestItems.add(new Item("payload", json));
                    }
                }
                Item[] array = new Item[logRequestItems.size()];
                array = logRequestItems.toArray(array);
                logRequestItems.clear();
            }
        });

        target = builder.build().target(server);
        headers = new MultivaluedHashMap<String, Object>();
        cookies = new ArrayList<Cookie>();
    }

    @SuppressWarnings("unchecked")
    public <T> T getHandler(Class<T> apiClass)
    {
        Object h = handlers.get(apiClass);
        if (h == null)
        {
            h = (T) WebResourceFactory.newResource(apiClass, target, false, headers, cookies, new Form());
            handlers.put(apiClass, h);
        }
        return (T) h;
    }

    public MultivaluedMap<String, Object> headers()
    {
        return headers;
    }

    public int getLastResponseCode()
    {
        return this.lastResponseCode;
    }

}
